package com.courir;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;

import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;

/**
 * Factories zum Erstellen von elementaren Datenstrukturen ohne Boilerplate (als
 * Ergänzung zu den Methoden von Guava). Die Klasse sollte immer direkt per
 * static-favorites-import von eclipse eingebunden werden.
 * <p>
 * Inhalt: Die Klasse enthält ausschließlich Factories für die am häufigsten
 * benötigten Datenstrukturen für die es keinen eleganten Ausdruck gibt, z.B.
 * ImmutableCollections oder die unmodifiableXXX-Methoden von java.util. Die
 * meisten Methoden sind massiv überladen damit möglichst wenig Namen durch die
 * static imports vorbelegt werden.
 * 
 * @author giese
 */
public class Predefined {

	/**
	 * Erzeugt ein neues Array.
	 */
	@SafeVarargs
	public static <E> E[] array(E... array) {
		return array;
	}

	/**
	 * Gibt die unveränderliche Liste (Nil) zurück.
	 * 
	 * @see ImmutableList#of()
	 */
	public static <E> ImmutableList<E> list() {
		return ImmutableList.of();
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object)
	 */
	public static <E> ImmutableList<E> list(E e1) {
		return ImmutableList.of(e1);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2) {
		return ImmutableList.of(e1, e2);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2, E e3) {
		return ImmutableList.of(e1, e2, e3);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object, Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2, E e3, E e4) {
		return ImmutableList.of(e1, e2, e3, e4);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object, Object, Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2, E e3, E e4, E e5) {
		return ImmutableList.of(e1, e2, e3, e4, e5);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object, Object, Object, Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2, E e3, E e4, E e5, E e6) {
		return ImmutableList.of(e1, e2, e3, e4, e5, e6);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object, Object, Object, Object,
	 *      Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2, E e3, E e4, E e5, E e6,
			E e7) {
		return ImmutableList.of(e1, e2, e3, e4, e5, e6, e7);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object, Object, Object, Object,
	 *      Object, Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2, E e3, E e4, E e5, E e6,
			E e7, E e8) {
		return ImmutableList.of(e1, e2, e3, e4, e5, e6, e7, e8);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object, Object, Object, Object,
	 *      Object, Object, Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2, E e3, E e4, E e5, E e6,
			E e7, E e8, E e9) {
		return ImmutableList.of(e1, e2, e3, e4, e5, e6, e7, e8, e9);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object, Object, Object, Object,
	 *      Object, Object, Object, Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2, E e3, E e4, E e5, E e6,
			E e7, E e8, E e9, E e10) {
		return ImmutableList.of(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object, Object, Object, Object,
	 *      Object, Object, Object, Object, Object)
	 */
	public static <E> ImmutableList<E> list(E e1, E e2, E e3, E e4, E e5, E e6,
			E e7, E e8, E e9, E e10, E e11) {
		return ImmutableList.of(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus den übergebenen Werten.
	 * 
	 * @see ImmutableList#of(Object, Object, Object, Object, Object, Object,
	 *      Object, Object, Object, Object, Object, Object, Object...)
	 */
	@SafeVarargs
	public static <E> ImmutableList<E> list(E e1, E e2, E e3, E e4, E e5, E e6,
			E e7, E e8, E e9, E e10, E e11, E e12, E... other) {
		return ImmutableList.of(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11,
				e12, other);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus der übergebenen Collection.
	 * 
	 * @see ImmutableList#copyOf(Collection)
	 */
	public static <E> ImmutableList<E> listOf(Collection<? extends E> elements) {
		return ImmutableList.copyOf(elements);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus dem übergebenen Array.
	 * 
	 * @see ImmutableList#copyOf(Iterator)
	 */
	public static <E> ImmutableList<E> listOf(E[] elements) {
		return ImmutableList.copyOf(elements);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus der übergebenen Iterable.
	 * 
	 * @see ImmutableList#copyOf(Iterable)
	 */
	public static <E> ImmutableList<E> listOf(Iterable<? extends E> elements) {
		return ImmutableList.copyOf(elements);
	}

	/**
	 * Erzeugt eine unveränderliche Liste aus dem übergebenen Iterator.
	 * 
	 * @see ImmutableList#copyOf(Iterator)
	 */
	public static <E> ImmutableList<E> listOf(Iterator<? extends E> elements) {
		return ImmutableList.copyOf(elements);
	}

	/**
	 * Gibt die unveränderliche Map (Nil) zurück.
	 * 
	 * @see ImmutableMap#of()
	 */
	public static <K, V> ImmutableMap<K, V> map() {
		return ImmutableMap.of();
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 * 
	 * @see ImmutableMap#of(K k1, V v1)
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1) {
		return ImmutableMap.of(k1, v1);
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 * 
	 * @see ImmutableMap#of(Object, Object)
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2) {
		return ImmutableMap.of(k1, v1, k2, v2);
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 * 
	 * @see ImmutableMap#of(Object, Object, Object, Object)
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3) {
		return ImmutableMap.of(k1, v1, k2, v2, k3, v3);
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 * 
	 * @see ImmutableMap#of(Object, Object, Object, Object, Object, Object)
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3, K k4, V v4) {
		return ImmutableMap.of(k1, v1, k2, v2, k3, v3, k4, v4);
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 * 
	 * @see ImmutableMap#of(Object, Object, Object, Object, Object, Object,
	 *      Object, Object)
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3, K k4, V v4, K k5, V v5) {
		return ImmutableMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3, K k4, V v4, K k5, V v5, K k6, V v6) {
		return ImmutableMap.<K, V> builder().put(k1, v1).put(k2, v2)
				.put(k3, v3).put(k4, v4).put(k5, v5).put(k6, v6).build();
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) {
		return ImmutableMap.<K, V> builder().put(k1, v1).put(k2, v2)
				.put(k3, v3).put(k4, v4).put(k5, v5).put(k6, v6).put(k7, v7)
				.build();
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) {
		return ImmutableMap.<K, V> builder().put(k1, v1).put(k2, v2)
				.put(k3, v3).put(k4, v4).put(k5, v5).put(k6, v6).put(k7, v7)
				.put(k8, v8).build();
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8,
			K k9, V v9) {
		return ImmutableMap.<K, V> builder().put(k1, v1).put(k2, v2)
				.put(k3, v3).put(k4, v4).put(k5, v5).put(k6, v6).put(k7, v7)
				.put(k8, v8).put(k9, v9).build();
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8,
			K k9, V v9, K k10, V v10) {
		return ImmutableMap.<K, V> builder().put(k1, v1).put(k2, v2)
				.put(k3, v3).put(k4, v4).put(k5, v5).put(k6, v6).put(k7, v7)
				.put(k8, v8).put(k9, v9).put(k10, v10).build();
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8,
			K k9, V v9, K k10, V v10, K k11, V v11) {
		return ImmutableMap.<K, V> builder().put(k1, v1).put(k2, v2)
				.put(k3, v3).put(k4, v4).put(k5, v5).put(k6, v6).put(k7, v7)
				.put(k8, v8).put(k9, v9).put(k10, v10).put(k11, v11).build();
	}

	/**
	 * Erzeugt eine unveränderliche Map aus den übergebenen Werten.
	 */
	public static <K, V> ImmutableMap<K, V> map(K k1, V v1, K k2, V v2, K k3,
			V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8,
			K k9, V v9, K k10, V v10, K k11, V v11, K k12, V v12) {
		return ImmutableMap.<K, V> builder().put(k1, v1).put(k2, v2)
				.put(k3, v3).put(k4, v4).put(k5, v5).put(k6, v6).put(k7, v7)
				.put(k8, v8).put(k9, v9).put(k10, v10).put(k11, v11)
				.put(k12, v12).build();
	}

	/**
	 * Erzeugt eine unveränderliche Map aus der übergebenen Map.
	 * 
	 * @see ImmutableMap#copyOf(Map)
	 */
	public static <K, V> ImmutableMap<K, V> mapOf(Map<K, V> map) {
		return ImmutableMap.copyOf(map);
	}

	/**
	 * Gibt einen readonly ListView um das Array zurück.
	 * <p>
	 * Anders als Arrays.asList unterstützt diese Methode als kein set, außerdem
	 * sind keine varargs-fehler möglich, da varargs immer über die list()
	 * methoden abgearbeitet werden sollten.
	 */
	public static <E> List<E> readonly(E[] elements) {
		if (elements.length == 0) {
			// leere arrays sind unveränderlich => Konstante verwenden
			return list();
		}
		return Collections.unmodifiableList(Arrays.asList(elements));
	}

	/**
	 * Gibt einen readonly-Wrapper um die Liste zurück.
	 * 
	 * @see Collections#unmodifiableList(List)
	 */
	public static <E> List<E> readonly(List<? extends E> list) {
		if (list instanceof ImmutableList) {
			// Sicherer Cast, da nicht schreibbar
			@SuppressWarnings("unchecked")
			List<E> l = (List<E>) list;
			return l;
		}
		return Collections.unmodifiableList(list);
	}

	/**
	 * Gibt einen readonly-Wrapper um die Map zurück.
	 * 
	 * @see Collections#unmodifiableMap(Map)
	 */
	public static <K, V> Map<K, V> readonly(Map<? extends K, ? extends V> map) {
		if (map instanceof ImmutableMap) {
			// Sicherer Cast, da nicht schreibbar
			@SuppressWarnings("unchecked")
			Map<K, V> m = (Map<K, V>) map;
			return m;
		}
		return Collections.unmodifiableMap(map);
	}

	/**
	 * Gibt einen readonly-Wrapper um die Collection zurück.
	 * 
	 * @see Collections#unmodifiableSet(Set)
	 */
	public static <E> Collection<E> readonly(Collection<? extends E> collection) {
		if (collection instanceof ImmutableCollection) {
			// Sicherer Cast, da nicht schreibbar
			@SuppressWarnings("unchecked")
			Collection<E> c = (Collection<E>) collection;
			return c;
		}
		return Collections.unmodifiableCollection(collection);
	}

	/**
	 * Gibt einen readonly-Wrapper um die Menge zurück.
	 * 
	 * @see Collections#unmodifiableSet(Set)
	 */
	public static <E> Set<E> readonly(Set<? extends E> set) {
		if (set instanceof ImmutableSet) {
			// Sicherer Cast, da nicht schreibbar
			@SuppressWarnings("unchecked")
			Set<E> s = (Set<E>) set;
			return s;
		}
		return Collections.unmodifiableSet(set);
	}

	/**
	 * Gibt einen readonly-Wrapper um die Map zurück.
	 * 
	 * @see Collections#unmodifiableSortedMap(Map)
	 */
	public static <K, V> SortedMap<K, V> readonly(SortedMap<K, ? extends V> map) {
		if (map instanceof ImmutableSortedMap) {
			// Sicherer Cast, da nicht schreibbar
			@SuppressWarnings("unchecked")
			SortedMap<K, V> m = (SortedMap<K, V>) map;
			return m;
		}
		return Collections.unmodifiableSortedMap(map);
	}

	/**
	 * Gibt einen readonly-Wrapper um die Menge zurück.
	 * 
	 * @see Collections#unmodifiableSortedSet(SortedSet)
	 */
	public static <E> SortedSet<E> readonly(SortedSet<E> set) {
		if (set instanceof ImmutableSortedSet) {
			// kein suppresswarnings nötig, da invariant
			return set;
		}
		return Collections.unmodifiableSortedSet(set);
	}

	/**
	 * Gibt die unveränderliche Menge (Nil) zurück.
	 * 
	 * @see ImmutableSet#of()
	 */
	public static <E> ImmutableSet<E> set() {
		return ImmutableSet.of();
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus den übergebenen Werten.
	 * 
	 * @see ImmutableSet#of(Object)
	 */
	public static <E> ImmutableSet<E> set(E e1) {
		return ImmutableSet.of(e1);
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus den übergebenen Werten.
	 * 
	 * @see ImmutableSet#of(Object, Object)
	 */
	public static <E> ImmutableSet<E> set(E e1, E e2) {
		return ImmutableSet.of(e1, e2);
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus den übergebenen Werten.
	 * 
	 * @see ImmutableSet#of(Object, Object, Object)
	 */
	public static <E> ImmutableSet<E> set(E e1, E e2, E e3) {
		return ImmutableSet.of(e1, e2, e3);
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus den übergebenen Werten.
	 * 
	 * @see ImmutableSet#of(Object, Object, Object, Object)
	 */
	public static <E> ImmutableSet<E> set(E e1, E e2, E e3, E e4) {
		return ImmutableSet.of(e1, e2, e3, e4);
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus den übergebenen Werten.
	 * 
	 * @see ImmutableSet#of(Object, Object, Object, Object, Object)
	 */
	public static <E> ImmutableSet<E> set(E e1, E e2, E e3, E e4, E e5) {
		return ImmutableSet.of(e1, e2, e3, e4, e5);
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus den übergebenen Werten.
	 * 
	 * @see ImmutableSet#of(Object, Object, Object, Object, Object, Object,
	 *      Object...)
	 */
	@SafeVarargs
	public static <E> ImmutableSet<E> set(E e1, E e2, E e3, E e4, E e5, E e6,
			E... others) {
		return ImmutableSet.of(e1, e2, e3, e4, e5, e6, others);
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus der übergebenen Collection.
	 * 
	 * @see ImmutableList#copyOf(Collection)
	 */
	public static <E> ImmutableList<E> setOf(Collection<? extends E> elements) {
		return ImmutableList.copyOf(elements);
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus dem übergebenen Array.
	 * 
	 * @see ImmutableSet#copyOf(Iterator)
	 */
	public static <E> ImmutableSet<E> setOf(E[] elements) {
		return ImmutableSet.copyOf(elements);
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus der übergebenen Iterable.
	 * 
	 * @see ImmutableSet#copyOf(Iterable)
	 */
	public static <E> ImmutableSet<E> setOf(Iterable<? extends E> elements) {
		return ImmutableSet.copyOf(elements);
	}

	/**
	 * Erzeugt eine unveränderliche Menge aus dem übergebenen Iterator.
	 * 
	 * @see ImmutableSet#copyOf(Iterator)
	 */
	public static <E> ImmutableSet<E> setOf(Iterator<? extends E> elements) {
		return ImmutableSet.copyOf(elements);
	}
}